package auth

import (
	"errors"
	"github.com/golang-jwt/jwt/v4"
	"strconv"
	"time"
)

// https://juejin.cn/post/7059184806906560543
type UserInfo struct {
	Id       uint64
	UserName string
	Email    *string
}

type CustomClaims struct {
	User *UserInfo
	jwt.RegisteredClaims
}

const TokenExpireDuration = time.Hour * 2

var signKey = []byte("yoursecret") // 生成签名的密钥

func RenewToken(claims *CustomClaims) (string, error) {
	// 若token过期不超过5分钟则给它续签
	dev := time.Now().Unix() - claims.ExpiresAt.Unix()
	if dev < 300 {
		return GenerateToken(claims.User)
	}
	return "", errors.New("登录已过期")
}

func GenerateToken(user *UserInfo) (string, error) {
	token := jwt.NewWithClaims(
		jwt.SigningMethodHS256,
		CustomClaims{
			User: user,
			RegisteredClaims: jwt.RegisteredClaims{
				// iss:定义发布者
				Issuer: "yang jun",
				// sub：该JWT所面向的用户
				Subject: "sub",
				// iat:签发时间
				IssuedAt: &jwt.NumericDate{
					Time: time.Now(),
				},
				// aud：接收该JWT的一方
				Audience: []string{},
				// exp:定义token的过期时间
				ExpiresAt: &jwt.NumericDate{
					Time: time.Now().Add(TokenExpireDuration),
				},
				// nbf:生效时间
				NotBefore: &jwt.NumericDate{
					Time: time.Now(),
				},
				// jti:编号
				ID: strconv.Itoa(int(user.Id)),
			},
		},
	)
	return token.SignedString(signKey)
}

func ParseToken(tokenString string) (*CustomClaims, error) {
	claims := &CustomClaims{}
	_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (interface{}, error) {
		return signKey, nil
	})
	// 若token只是过期claims是有数据的，若token无法解析claims无数据
	return claims, err
}

// 第二种方法通过jwt.ParseWithClaims返回的Token结构体取出Claims结构体
func ParseToken2(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(t *jwt.Token) (interface{}, error) {
		return signKey, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("token无法解析")
}
